﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ElevatorDispatching.WinForms
{
    public partial class MainForm : Form
    {
        /// <summary>
        /// 楼层数量
        /// </summary>
        private const int FLOOR_COUNT = 20;

        /// <summary>
        /// 电梯/轿厢数量
        /// </summary>
        private const int CAGE_COUNT = 4;

        private ElevatorDispatcher elevatorDispatcher;

        /// <summary>
        /// 楼层标识
        /// </summary>
        private IDictionary<int, Label> floorLabels = new Dictionary<int, Label>();

        /// <summary>
        /// 厅外上行呼梯按钮
        /// </summary>
        private IDictionary<int, Button> outsideCallUpButtons = new Dictionary<int, Button>();

        /// <summary>
        /// 厅外下行呼梯按钮
        /// </summary>
        private IDictionary<int, Button> outsideCallDownButtons = new Dictionary<int, Button>();

        /// <summary>
        /// 轿内呼层按钮
        /// </summary>
        private IDictionary<int, Button>[] insideCallFloorButtons = new IDictionary<int, Button>[CAGE_COUNT];

        private ElevatorInfoDataSource[] cageInfoDataSources = new ElevatorInfoDataSource[CAGE_COUNT];

        /// <summary>
        /// 轿厢内部的信息显示标签
        /// </summary>
        private Label[] innerCageInfoLabels = new Label[CAGE_COUNT];

        /// <summary>
        /// 楼层外部的轿厢信息显示标签
        /// </summary>
        private Label[] outerCageInfoLabels = new Label[CAGE_COUNT];

        public MainForm()
        {
            InitializeComponent();
            InitializeElevatorDispatcher();
            InitializeAdditionalComponent();
        }

        #region Helpers for window moving

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wparam, int lparam);

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (e.Button == MouseButtons.Left) // 按下的是鼠标左键            
            {
                Capture = false; // 释放鼠标，使能够手动操作                
                SendMessage(Handle, 0x00A1, 2, 0); // 拖动窗体            
            }
        }

        #endregion

        private void InitializeElevatorDispatcher()
        {
            IFloorGroupBuilder builder = new DefaultFloorGroupBuilder(1, FLOOR_COUNT);
            elevatorDispatcher = new ElevatorDispatcher(builder, CAGE_COUNT);
        }

        private void InitializeAdditionalComponent()
        {
            for (int i = 0; i < CAGE_COUNT; i++)
            {
                insideCallFloorButtons[i] = new Dictionary<int, Button>();
                cageInfoDataSources[i] = new ElevatorInfoDataSource(elevatorDispatcher.Elevators[i]);

                #region 轿厢信息显示标签

                var innerCageInfoLabel = new Label
                {
                    Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right,
                    AutoSize = true,
                    TextAlign = ContentAlignment.MiddleCenter,
                    Padding = new Padding(0),
                    Margin = new Padding(0),
                    Font = new Font(FontFamily.GenericSansSerif, Font.Size * 2),
                    Name = $"innerCageInfoLabel_{i}"
                };

                innerCageInfoLabel.DataBindings.Add(new Binding(nameof(Label.Text), cageInfoDataSources[i], nameof(ElevatorInfoDataSource.InnerInfoText)));

                innerCageInfoLabels[i] = innerCageInfoLabel;
                cageInfoPanel.Controls.Add(innerCageInfoLabel, i, FLOOR_COUNT - elevatorDispatcher.BaseFloor.Id);
                elevatorDispatcher.Elevators[i].PropertyChanged += (sender, e) =>
                {
                    if (!(sender is Elevator cage) || e.PropertyName != nameof(Elevator.CurrentFloor)) return;
                    cageInfoPanel.Controls.Add(innerCageInfoLabels[cage.Id], cage.Id, FLOOR_COUNT - cage.CurrentFloor.Id);
                };

                var outerCageInfoLabel = new Label
                {
                    Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right,
                    AutoSize = true,
                    TextAlign = ContentAlignment.MiddleCenter,
                    Padding = new Padding(0),
                    Margin = new Padding(0),
                    Font = new Font(FontFamily.GenericSansSerif, Font.Size * 1.5f),
                    Name = $"outerCageInfoLabel_{i}"
                };

                outerCageInfoLabel.DataBindings.Add(new Binding(nameof(Label.Text), cageInfoDataSources[i], nameof(ElevatorInfoDataSource.OuterInfoText)));

                outerCageInfoLabels[i] = outerCageInfoLabel;
                cageInfoPanel.Controls.Add(outerCageInfoLabel, i, FLOOR_COUNT);

                #endregion
            }

            for (int i = 1; i <= FLOOR_COUNT; i++) // 按楼层初始化 UI 控件
            {

                #region 楼层标签

                var floorLabel = new Label
                {
                    Name = $"lblFloor_{i}",
                    Text = $"{i}",
                    Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right,
                    AutoSize = true,
                    TextAlign = ContentAlignment.MiddleRight,
                };

                floorLabels[i] = floorLabel;
                outsideCallPanel.Controls.Add(floorLabel, 0, FLOOR_COUNT - i);

                #endregion

                #region 上行呼梯按钮

                if (elevatorDispatcher.Floors[i].CanCallUp)
                {
                    var callUpButton = new Button
                    {
                        Name = $"btnCallUp_{i}",
                        Text = "∧",
                        Tag = i,
                        Anchor = AnchorStyles.Top | AnchorStyles.Bottom
                    };
                    callUpButton.Click += CallCageUpButton_Click;
                    var enableBinding = new Binding(nameof(Enabled), elevatorDispatcher.Floors[i].CallUpButton, nameof(ButtonState.IsPressed));
                    enableBinding.Format += EnableBinding_Format;
                    callUpButton.DataBindings.Add(enableBinding);

                    outsideCallUpButtons[i] = callUpButton;
                    outsideCallPanel.Controls.Add(callUpButton, 1, FLOOR_COUNT - i);
                }

                #endregion

                #region 下行呼梯按钮

                if (elevatorDispatcher.Floors[i].CanCallDown)
                {
                    var callDownButton = new Button
                    {
                        Name = $"btnCallDown_{i}",
                        Text = "∨",
                        Tag = i,
                        Anchor = AnchorStyles.Top | AnchorStyles.Bottom
                    };
                    callDownButton.Click += CallCageDownButton_Click;
                    var enableBinding = new Binding(nameof(Enabled), elevatorDispatcher.Floors[i].CallDownButton, nameof(ButtonState.IsPressed));
                    enableBinding.Format += EnableBinding_Format;
                    callDownButton.DataBindings.Add(enableBinding);

                    outsideCallDownButtons[i] = callDownButton;
                    outsideCallPanel.Controls.Add(callDownButton, 2, FLOOR_COUNT - i);
                }

                #endregion

                #region 轿厢内呼层按钮

                for (int cageId = 0; cageId < CAGE_COUNT; cageId++)
                {
                    var callFloorButton = new Button
                    {
                        Name = $"btnCallFloor_{cageId}_{i}",
                        Text = Convert.ToString(i),
                        Tag = new { CageId = cageId, FloorId = i },
                        Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Bottom
                    };
                    callFloorButton.Click += CallFloorButton_Click;
                    var enableBinding = new Binding(nameof(Enabled), elevatorDispatcher.Elevators[cageId].FloorButtonStates[i], nameof(ButtonState.IsPressed));
                    enableBinding.Format += EnableBinding_Format;
                    callFloorButton.DataBindings.Add(enableBinding);

                    insideCallFloorButtons[cageId][i] = callFloorButton;
                    insideCallPanel.Controls.Add(callFloorButton, cageId, FLOOR_COUNT - i);
                }

                #endregion
            }
        }

        private void EnableBinding_Format(object sender, ConvertEventArgs e)
        {
            if (e.DesiredType == typeof(bool)) e.Value = !Convert.ToBoolean(e.Value);
        }

        private void CallFloorButton_Click(object sender, EventArgs e) => CallFloor(((dynamic)((Button)sender).Tag).CageId, ((dynamic)((Button)sender).Tag).FloorId);

        private void CallCageUpButton_Click(object sender, EventArgs e) => CallCage(Direction.Up, Convert.ToInt32(((Button)sender).Tag));

        private void CallCageDownButton_Click(object sender, EventArgs e) => CallCage(Direction.Down, Convert.ToInt32(((Button)sender).Tag));

        private void CallFloor(int cageId, int floorId)
        {
            var signal = new CallFloorSignal(cageId, floorId);
            elevatorDispatcher.HandleCallSignal(signal);
        }

        private void CallCage(Direction direction, int floorId)
        {
            var signal = new CallCageSignal(direction, floorId);
            elevatorDispatcher.HandleCallSignal(signal);
        }
    }
}
